#!/usr/bin/env node
// @ts-nocheck
require('./lib/cmdInc');

const { _: args, ...cmdOpts } = require('minimist')(process.argv.slice(2));
const concurrently = require('concurrently');
const Spinnies = require('dreidels');
const chalk = require('chalk');
const { kill } = require('cross-port-killer');
const streams = require('memory-streams');
const readline = require('readline');

const { defaultValue } = require('./lib/defaultValue');
const { DomainConfig, getSingleDomains } = require('./lib/domain.config');
// const { autoUpgrade } = require('./lib/checkVersion');
const { colors } = require('./lib/config');
const { execSync } = require('child_process');

const logStream = new streams.ReadableStream('');
const spinnies = new Spinnies();

process.on('SIGINT', () => spinnies.stopAll());

spinnies.statusRegistry.configureStatus('wait', {
  textColor: 'green',
  isStatic: true,
  isDone: false,
  prefix: '🔸',
});

const errInfo = {};

function clearConsole() {
  process.stdout.write(process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H');
  // console.log(`\n${chalk.gray('─'.repeat(Math.max(w - 10, w / 2)))}\n`)
  console.log(
    chalk.gray(`
❓ 按领域构建：
    域可选值: apphub components modules voucher-common metapage hkj
    dev -a                    并行编译所有域   👍
    dev modules components    并行编译多个域   👍
    dev apphub                单独编译指定域   👍
`)
  );

  if (Object.keys(errInfo).length > 0) {
    console.log(chalk.bgBlack(chalk.yellowBright('编译错误\n')));
    console.log(Object.values(errInfo).join('\n'), '\n');
  }
}

/* --------------------------------- 逐行扫描日志 --------------------------------- */

const readLineStream = readline.createInterface({ input: logStream });

const shortPath = (line) => line.replace(process.cwd() + '/', '');
const getDomainFromLine = (line) => {
  let matcher = line.match(/(\[.+?\])\s.+/);
  return matcher ? matcher[1] : null;
};

readLineStream.on('line', (text) => {
  // 清除颜色
  let newText = text.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');

  if (/\[.+?\]\s*<s>/.test(newText)) {
    newText.replace(/\[(.+?)\]\s*<s>\s*\[webpack\.Progress\](.+)/, (_, domain, progressInfo) => {
      if (/1%/.test(progressInfo)) {
        // 清理错误信息
        errInfo[`[${domain}]`] && delete errInfo[`[${domain}]`];
      }
      if (/100%/.test(progressInfo)) {
        spinnies.hasActiveSpinners() &&
          spinnies.update(domain, {
            status: 'wait',
            text: format(domain, 'http://localhost:' + DomainInfo[domain].port),
          });
        clearConsole();
      } else {
        spinnies.hasActiveSpinners() &&
          spinnies.update(domain, { status: 'spinning', text: format(domain, progressInfo) });
      }
    });
  }
  if (/:\d+:\d+:?\s+error:?\s+/.test(newText)) {
    let reformat = newText.replace(/(\[.+?\])\s+(.+\d+:\d+):?\s+error:?\s+(.+)/, (_, $1, $2, $3) => {
      return `${$1} ${chalk.yellowBright(shortPath($2))} ${chalk.red('error')} ${chalk.cyan(shortPath($3))}`;
    });
    errInfo[getDomainFromLine(newText)] = reformat;
  }
});

const DomainInfo = {};

const format = (domain, info) => {
  let _domain = chalk.hex(DomainInfo[domain].color).bgBlack.bold(domain);
  let space = ' '.repeat(16).slice(0, 16 - domain.length);
  _domain = _domain + space;
  return `${_domain} ${DomainInfo[domain].port}   ${info}\n`;
};

/* ---------------------------------- 环境变量 ---------------------------------- */

let MODFED = defaultValue('MODFED', true);
let HTML = defaultValue('HTML', true);
let PORT = process.env.PORT || 8080;
let ONLY_THIRDLIB_REMOTE = defaultValue('ONLY_THIRDLIB_REMOTE', false);
let OPEN_BROWSER = defaultValue('OPEN_BROWSER', false);
const NODE_OPTIONS = '--max-old-space-size=8192';

async function main() {
  // 自动升级
  // await autoUpgrade();

  // 运行cexp
  execSync('cexp', { stdio: 'inherit' });

  clearConsole();

  /* --------------------------------- 解析命令选项 --------------------------------- */
  cmdOpts.m && (MODFED = false);
  cmdOpts.t && (ONLY_THIRDLIB_REMOTE = true);

  if (cmdOpts.lib) {
    MODFED = true;
    HTML = false;
    OPEN_BROWSER = false;
  }

  /* --------------------------------- 解析领域列表 --------------------------------- */

  let domains = ['default'];



  const singleDomains = getSingleDomains();

  // 以src 开头的领域目录
  const localDomains = Object.fromEntries(
    Object.entries(DomainConfig).filter(([domainName, domainPath]) => {
      return /^(src|thirdlib)/.test(domainPath) && !singleDomains.includes(domainName);
    })
  );

  const allDomains = ['default'].concat(Object.keys(localDomains));
  const inputDomains = args.length > 0 ? allDomains.filter((m) => args.includes(m)) : [];
  const useInputDomains = inputDomains.length > 0;
  const hasBuildAllOption = cmdOpts.all || cmdOpts.a;

  useInputDomains && (domains = inputDomains);

  cmdOpts.t && (domains = ['default']);
  cmdOpts.m && (domains = ['default']);

  if (hasBuildAllOption) {
    domains = allDomains;
    HTML = true;
    OPEN_BROWSER = false;
  }

  /* --------------------------------- 防止端口占用 --------------------------------- */
  await killPorts(domains);

  /* ---------------------------------- 执行命令 ---------------------------------- */

  concurrentlyRun(domains);
}

/* ----------------------------------- start ----------------------------------- */
main();

async function killPorts(domains) {
  return Promise.all(domains.map((domain, index) => kill(PORT - index))).catch((err) => { });
}

function concurrentlyRun(domains) {
  const commands = getCommonds(domains);
  const option = {
    outputStream: {
      write(text) {
        logStream.append(text);
      },
    },
  };

  concurrently(commands, option);
}

function getCommonds(domains) {
  return domains.map((domain, index) => {
    const port = PORT - index;
    const color = colors[index % colors.length];
    DomainInfo[domain] = { port, color };

    const env = { domain, PORT: port, OPEN_BROWSER, HTML, MODFED, ONLY_THIRDLIB_REMOTE, NODE_OPTIONS };
    const envstr = Object.entries({ domain, PORT: port, OPEN_BROWSER, HTML, MODFED, ONLY_THIRDLIB_REMOTE })
      .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
      .join(' ');
    const command = 'npx cjet dev';

    // 添加 spinner
    spinnies.add(domain, { text: format(domain, envstr + ' ' + command) });

    return {
      command: 'npx cjet dev',
      name: domain,
      raw: false,
      env: {
        ...process.env,
        ...{ domain, PORT: port, OPEN_BROWSER, HTML, MODFED, ONLY_THIRDLIB_REMOTE, NODE_OPTIONS },
      },
    };
  });
}
